/*
 *  파일 변경 전 백업파일 저장 -  수정파일이름_backup.txt
 *  파일 변경 후 변경된 파일 저장 -  수정파일이름.txt
 *  파일 변경 내용을 저장하여 기록 -  파일수정정보.txt
 *  세 파일을 압축하여  (년월일-시분)-이름-수정파일이름.zip로 남기세요
 */

/* 	파일 내용을 변경하고 커밋을 하기 전에 modifier에 이름 추가
 * 
 *  ( 년 월 일 시 분 ) - 이름 - 수정 부분(간략히)
 *  ex) 20141228_1342 - 김영우 - 스레드 컨트롤
 *  
 *  ** 기록은 3명까지 남기세요 **
 */

/* 
 *  MODIFIER
 *  
 *  20141228_0238 - 김영우 - run()
 */

package server;

import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;

import voSetting.LoginVO;
import voSetting.PortVO;
import voSetting.SetMyInfoVO;
import voSetting.StateSetVO;

public class ServerThread extends Thread {
	Server server;
	Socket socket;
	
	InputStream is;
	ObjectInputStream ois;	
	OutputStream os;
	ObjectOutputStream oos;
	
	private final int ROOM_TYPE_IS_CHAT = 1;
	private final int ROOM_TYPE_IS_FILE = 2;
	
	boolean flag = true;
	
	public ServerThread(Server server, Socket s){	// 서버와 소켓을 가져옴
		this.server = server;
		this.socket = s;			
		start();					// 서버스레드를 시작시킴
	}
	
	public void run(){
		try{
			// 입 출력을 위한 스트림 생성
			os = socket.getOutputStream();
			oos = new ObjectOutputStream(os);	
			
			is = socket.getInputStream();		
			ois = new ObjectInputStream(is);	 
			
			while (flag) {				// 실제 데이터가 처리되는 부분
				SetMyInfoVO smvo = null;
				
				StateSetVO ssvo = (StateSetVO) ois.readObject(); 	// 1. 중개 정보를 가지는 vo객체
				
				switch (ssvo.getVo_type() ) {	// 전달받은 번호에 따라서 데이터 처리
					case 1:	// 인증 및 로그인 처리
						LoginProcess lp = new LoginProcess();
						System.out.println("\n\n서버측 로그인 요청 응답 받음");
						LoginVO lvo = (LoginVO) ssvo.getVo();
						
						Iterator<String> iter = server.getClients().keySet().iterator();
						
						boolean connecter = false;
						
						while(iter.hasNext()){
							String connect_id = iter.next();
							if(connect_id.equals(lvo.getM_id())){	// 접속된 아이디가 있는지 검사	check the id
								connecter = true;
								break;
							}
						}
						
						if(connecter){	// 기존 접속 정보를 종료시기고 새로 접속시킨다	exit the previously connected client, and create new client connection.
							System.out.println("기존 접속자가 접속을 시도함");
							// 로그인 처리	login process
							ssvo.setVo(lp.logincheck((LoginVO) ssvo.getVo()));	// 로그인 성공 또는 실패 저장
							if((boolean) ssvo.getVo()){
								System.out.println("기존 접속자가 접속 성공 - 접속에 성공한 아이디  : " + lvo.getM_id());
								smvo = server.getUser_information().get(lvo.getM_id());
								smvo.setLogin_state(false);
								ssvo.setVo(smvo);	// 기존 연결된 클라이언트에 보낼 값	sending a value to previously connected client
								ssvo.setVo_type(4);
								System.out.println(" 기존 연결을 끊기 위해 데이터를 전송합니다");
								System.out.println(server.getClients().size());
								server.getClientServer(smvo.getM_id()).oos.writeObject(ssvo);// 연결을 끊기위한 데이터를 보냄
								server.getClientServer(smvo.getM_id()).oos.flush();
								//server.getClientServer(lvo.getM_id()).oos.flush();
								
								System.out.println("이전 접속 정보를 지웁니다.");
								server.getClientServer(lvo.getM_id()).flag = false;	// 서버의 와일문 종료
								
								server.getClients().remove(lvo.getM_id());	// 기존에 접속된 서버스레드를 지운다 연결 종료
								server.dropUser_information(lvo.getM_id()); // 기존 저장된 유저정보를 삭제
								updateServerList();
								System.out.println(server.getClients().size());
								sleep(1500);
								System.out.println("새로운 로그인을 시도합니다");
								smvo = lp.find_user_information(lvo);	// 받은 아이디로 유저정보를 검색
								smvo.setLogin_state(true);
								ssvo.setVo(smvo);	// 새로운 로그인 시도
								ssvo.setVo_type(1);
								// 서버에 접속정보 저장	storing the access information to the server
								System.out.println("새로운 접속 정보를 저장합니다");
								System.out.println("새로 접속한 유저 아이디 : " + lvo.getM_id());
								server.addClients(lvo.getM_id(), ServerThread.this);
								server.addUser_information(smvo);	// 회원 정보를 리스트에 저장
								System.out.println("서버에 정보를 업데이트 합니다");
								updateServerList();
							}
							else{	// 정보가 올바르지 않을때
								smvo = new SetMyInfoVO();
								smvo.setLogin_state(false);
								ssvo.setVo(smvo);
							}
						}
						else{		// 첫 접속시 실행
							ssvo.setVo(lp.logincheck((LoginVO) ssvo.getVo()));
							System.out.println("로그인을 시도합니다.");
							// 로그인 처리한다	login process
							if((boolean) ssvo.getVo()){
								// 서버에 접속정보 저장	storing the access information to the server
								System.out.println("접속 정보를 저장합니다.");
								smvo = lp.find_user_information(lvo);	// 받은 아이디로 유저정보를 검색
								smvo.setLogin_state(true);
								ssvo.setVo(smvo);
								System.out.println("새로 접속한 유저 아이디 : " + lvo.getM_id());
								server.addClients(lvo.getM_id(), ServerThread.this);
								server.addUser_information(smvo);	// 회원 정보를 리스트에 저장
								
								updateServerList();
							}
							else{	// 정보가 올바르지 않을때
								System.out.println("접속 미스입니다");
								smvo = new SetMyInfoVO();
								smvo.setLogin_state(false);
								ssvo.setVo(smvo);
							}
						}
						System.out.println("서버 -> 클라이언트로 데이터를 보냅니다.");
						oos.writeObject(ssvo);
						System.out.println("서버측 로그인 요청 응답 보냄\n\n");
						oos.flush();
						break;
					case 2:	// 룸 생성
						boolean transmit_message = false;
						PortVO pvo = (PortVO) ssvo.getVo();
						pvo.setPort(server.getPort_number());
						System.out.println(pvo.getInvitation_user_list().get(0));
						if(pvo.getCommand() == ROOM_TYPE_IS_CHAT){
							System.out.println(" ------------------------------채팅룸 생성을 실행--------------------------------");
							transmit_message = add_room(ROOM_TYPE_IS_CHAT, pvo.getPort());	// 룸 생성 및 실행
							System.out.println("transmit_message 값 : " + transmit_message);
						}
						else if(pvo.getCommand() == ROOM_TYPE_IS_FILE){
							System.out.println(" ------------------------------파일룸 생성을 실행--------------------------------");
							transmit_message = add_room(ROOM_TYPE_IS_FILE, pvo.getPort());	// 룸 생성 및 실행
							System.out.println("transmit_message 값 : " + transmit_message);
						}
						else{
							
						}
						
						if(transmit_message){	// true 이면 유저들에게 서버의 포트번호 및 UI를 실행
							sleep(2000);		// 정렬해줌
							System.out.println("정보를 보내줍니다");
							transmit_room_information(pvo);
							System.out.println("정보 보내기 끝");
							update_file_chat_list();
						}
						break;
					case 3:	// 상태 정보
						// 변경된 상태 정보를 저장 -db내 메신저 상태정보 필요
						break;
					case 4:	// 로그아웃
						// 상태정보 0으로 초기화
						// 대화상태 및 파일전송시 스레드 정지 / 
						smvo = (SetMyInfoVO) ssvo.getVo();
						smvo.setLogin_state(false);
						
						server.getClients().remove(smvo.getM_id());	// 기존에 접속된 서버스레드를 지운다 연결 종료
						server.dropUser_information(smvo.getM_id()); // 기존 저장된 유저정보를 삭제
						
						updateServerList();
						
						oos.writeObject(ssvo);
						oos.flush();
						flag = false;
						return ;
				}
			}	// close while
			
		}catch(Exception e){
			
		}
	}
	
	// 첫번째 값은 포트번호 두번째 값은 채팅/ 파일 서버
	public boolean add_room(int room_type, int port_number){
		boolean return_type = false;
		if(ROOM_TYPE_IS_CHAT == room_type){
			Vector<IntermediateServer> chat_intermediate_controller = this.server.getChat_intermediate_controller();
			System.out.println("오픈된 채팅 중개 서버 : " + chat_intermediate_controller.size());
			if(chat_intermediate_controller.size() != 0){
				for(int i = 0; i< chat_intermediate_controller.size(); i++){	// 오픈된 채팅 중개서버의 개수만큼 루프
					System.out.println("포문 안 (" + i + ") 번째 루프 시작");
					
					if(chat_intermediate_controller.get(i).manage_port_size()){ // 새 포트를 만드는가 또는 추가하는가 true or false
						System.out.println("중개서버 " + i + "번에 새로운 채팅서버가 생성 됩니다. \n 생성된 채팅서버의 포트번호 : " + port_number);
						return_type = chat_intermediate_controller.get(i).add_manage_port(port_number);
						System.out.println("return_type : " + return_type);
						break;
					}
					else{
						System.out.println("오픈된 중개서버중 남은 공간이 없어 새로운 중개서버를 생성합니다.");
						return_type = add_intermediate_controller(room_type, port_number, chat_intermediate_controller);
						System.out.println("return_type : " + return_type);
						break;
					}
					
				}// end for;
			}
			else{// 이미 룸이 MAX상태이면 새로운 관리 서버를 생성하라는 명령
				System.out.println("오픈된 중개서버가 없어 새로운 중개서버를 생성합니다.");
				return_type = add_intermediate_controller(room_type, port_number, chat_intermediate_controller);
			}
			
		}
		else if(ROOM_TYPE_IS_FILE == room_type){
			Vector<IntermediateServer> file_intermediate_controller = this.server.getFile_intermediate_controller();
			System.out.println("오픈된 파일 중개 서버 : " + file_intermediate_controller.size());
			if(file_intermediate_controller.size() != 0){
				
				for(int i = 0; i< file_intermediate_controller.size(); i++){
					System.out.println("포문 안 (" + i + ") 번째 루프 시작");
					if(file_intermediate_controller.get(i).manage_port_size()){ // 생성시 0이 반환되면 정상
						System.out.println("중개서버 " + i + "번에 새로운 파일서버가 생성 됩니다. \n 생성된 파일서버의 포트번호 : " + port_number);
						return_type = file_intermediate_controller.get(i).add_manage_port(port_number);
						System.out.println("return_type : " + return_type);
						break;
					}
					else{// 이미 룸이 MAX상태이면 새로운 관리 서버를 생성하라는 명령
						System.out.println("오픈된 중개서버중 남은 공간이 없어 새로운 중개서버를 생성합니다.");
						return_type = add_intermediate_controller(room_type, port_number, file_intermediate_controller);
						System.out.println("return_type : " + return_type);
						break;
					}
				}// end for
			}
			else{// 이미 룸이 MAX상태이면 새로운 관리 서버를 생성하라는 명령
				System.out.println("오픈된 중개서버가 없어 새로운 중개서버를 생성합니다.");
				return_type = add_intermediate_controller(room_type, port_number, file_intermediate_controller);
			}
		}
		return return_type;
	}
	
	// 검사 필요	// 첫번째 값은 룸 타입,  두번째 값은 포트번호, 세번째 값은 채팅/파일 서버, 네번쨰 값은  채팅/파일 관리 서버
	private boolean add_intermediate_controller(int room_type, int port_number, Vector<IntermediateServer> intermediate_controller){
		boolean value = false;	// 0이 반환되면 정상
		if(ROOM_TYPE_IS_CHAT == room_type){
			System.out.println("새로운 중개서버를 생성합니다.");
			intermediate_controller.add(new IntermediateServer(ROOM_TYPE_IS_CHAT, this.server));
			System.out.println("새로 만들어진 중개서버에 채팅서버를 생성합니다. 생성된 채팅서버의 포트번호 : " + port_number);
			value = intermediate_controller.get(intermediate_controller.size()-1).add_manage_port(port_number); // 첫번째 값은 포트번호
			System.out.println("모든 작업이 정상적으로 처리되었습니다.");
			return true;	// 정상 실행
		}
		else if(ROOM_TYPE_IS_FILE == room_type){
			System.out.println("새로운 중개서버를 생성합니다.");
			intermediate_controller.add(new IntermediateServer(ROOM_TYPE_IS_FILE, this.server));
			System.out.println("새로 만들어진 중개서버에 파일서버를 생성합니다. 생성된 파일서버의 포트번호 : " + port_number);
			value = intermediate_controller.get(intermediate_controller.size()-1).add_manage_port(port_number); // 첫번째 값은 포트번호
			System.out.println("모든 작업이 정상적으로 처리되었습니다.");
			return true;	// 정상 실행
		}
		
		System.out.println("모든 작업이 정상적으로 처리되지 않았습니다.");
		return false;	// 비정상 실행
	}
	
	private void update_file_chat_list(){
		server.resetRoom_info();
		
		for(int i = 0; i < server.getChat_intermediate_controller().size(); i++){
			IntermediateServer is = server.getChat_intermediate_controller().get(i);
			Iterator<Integer> iter = is.manage_port.keySet().iterator();
			while (iter.hasNext()) {
				int key = iter.next();
				String value = "";
				if(is.getNow_type() == 1){
					value = "채팅 서버 (포트번호 : " + key + " )";
				}else if(is.getNow_type() == 2){
					value = "파일 서버 (포트번호 : " + key + " )";
				}
				server.setRoom_info(value);
			}// end while
		}// end for
		
		for(int i = 0; i < server.getFile_intermediate_controller().size(); i++){
			IntermediateServer is = server.getFile_intermediate_controller().get(i);
			Iterator<Integer> iter = is.manage_port.keySet().iterator();
			while (iter.hasNext()) {
				int key = iter.next();
				String value = "";
				if(is.getNow_type() == 1){
					value = "채팅 서버 (포트번호 : " + key + " )";
				}else if(is.getNow_type() == 2){
					value = "파일 서버 (포트번호 : " + key + " )";
				}
				server.setRoom_info(value);
			}// end while
		}// end for
	}
	
	private void updateServerList() {
		server.resetUser_list();
		HashMap<String, SetMyInfoVO> user_information = this.server.getUser_information();
		Iterator<String> iter =  user_information.keySet().iterator();
		
		while(iter.hasNext()){
			String next_key = iter.next();
			server.setUser_list(user_information.get(next_key).getE_name() + " " 
					+ user_information.get(next_key).getE_position());
		}
	}
	public void transmit_room_information(PortVO pvo){
		System.out.println("메시지를 보내기 시작합니다!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
		System.out.println("pvo 아이디 값 : " +  pvo.getM_id());
		System.out.println("pvo 에 저장된 개수 " + pvo.getInvitation_user_list().size());
		HashMap<String, ServerThread> clients = this.server.getClients();
		Iterator<String> iter = clients.keySet().iterator();
		pvo.getInvitation_user_list().add(pvo.getM_id());
		while(iter.hasNext()){
			String key = iter.next();
			
			for(int i = 0; i< pvo.getInvitation_user_list().size(); i++){
				System.out.println("포문 내에서 찍음  :  " + pvo.getInvitation_user_list().get(i));
				if(key.equals(pvo.getInvitation_user_list().get(i))){	// 아이디를 비교해서 같으면 메시지를 보내줌
					
					StateSetVO ssvo = new StateSetVO();
					PortVO send_pvo = new PortVO();
					send_pvo.setPort(pvo.getPort());	// 포트번호 셋팅
					System.out.println("포트번호" + pvo.getPort());
					send_pvo.setCommand(pvo.getCommand());// 파일 또는 채팅 번호 셋팅
					send_pvo.setM_id(pvo.getM_id());
					System.out.println("받는유저 : " + pvo.getInvitation_user_list().get(i));
					ssvo.setVo(send_pvo);	// StateSetVO에 데이터를 집어넣고 파일을 보낸다
					ssvo.setVo_type(2);		// 전달 번호는 2번 (룸 생성)
					try {
						clients.get(key).oos.writeObject(ssvo);	// 각각 아이디마다 할당된 서버스레드로 ssvo를 보냄
						clients.get(key).oos.flush();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					break;
				}// end if
			}// end for
		}// end while
		System.out.println("데이터 전달을 완료합니다.!!!!!");
	}

	public ObjectOutputStream getOos() {
		return oos;
	}

	public void setOos(ObjectOutputStream oos) {
		this.oos = oos;
	}
}

